using System;
using System.Collections;
using System.IO;
using System.Text;
using System.Threading;
using System.Reflection;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Channels;
using System.Data;
using System.Security;
using System.Security.Permissions;
using System.Security.Policy;

namespace PluginManager
{
    /// <summary>
    /// The PluginManager tracks changes to the plugin directory, handles reloading of the plugins,
    /// and monitoring the plugin directory.
    /// </summary>
    public class PluginManager
    {
        /// <summary>
        /// The <see cref="AppDomain"/> of this PluginManager
        /// </summary>
        protected AppDomain pluginAppDomain = null;
        /// <summary>
        /// The <see cref="AppDomainSetup"/> of this PluginManager
        /// </summary>
        protected AppDomainSetup pluginAppDomainSetup = null;
        /// <summary>
        /// The <see cref="RemoteClassLoader"/> of this PluginManager
        /// </summary>
        protected RemoteClassLoader remoteLoader = null;
        /// <summary>
        /// The <see cref="LocalClassLoader"/> of this PluginManager
        /// </summary>
        protected LocalClassLoader localLoader = null;

        /// <summary>
        /// The Directory where the plugins are located
        /// </summary>
        public static string pluginDirectory = null;
        /// <summary>
        /// The Path to the file that says which plugins should load at startup
        /// </summary>
        public static string pluginFile = Path.Combine (Directory.GetCurrentDirectory() ,"plugins.txt");
        /// <summary>
        /// Creates a new PluginManager using "lib" as the plugin path
        /// </summary>
        public PluginManager() : this("lib") { }
        /// <summary>
        /// Creates a new PluginManager
        /// </summary>
        /// <param name="pluginRelativePath">The path relative to the current directory of the plugin</param>
        public PluginManager(string pluginRelativePath)
        {
            string assemblyLoc = Assembly.GetExecutingAssembly().Location;
            string currentDirectory = assemblyLoc.Substring(0, assemblyLoc.LastIndexOf(Path.DirectorySeparatorChar) + 1);
            pluginDirectory = Path.Combine(currentDirectory, pluginRelativePath);
            if (!pluginDirectory.EndsWith(Path.DirectorySeparatorChar.ToString()))
            {
                pluginDirectory = pluginDirectory + Path.DirectorySeparatorChar;
            }
            if (!Directory.Exists(pluginDirectory))
                Directory.CreateDirectory(pluginDirectory);

            localLoader = new LocalClassLoader(pluginDirectory);
        }
        /// <summary>
        /// Loads all of the Assemblies in the <see cref="pluginDirectory"/>
        /// </summary>
        public void LoadUserAssemblies()
        {
            DirectoryInfo directory = new DirectoryInfo(pluginDirectory);
            foreach (FileInfo file in directory.GetFiles("*.dll"))
            {
                try
                {
                    localLoader.LoadAssembly(file.FullName);
                }
                catch (PolicyException e)
                {
                    throw new PolicyException(String.Format("Cannot load {0} - code requires privilege to execute", file.Name), e);
                }
            }
        }
        /// <summary>
        /// Gets all of the Attributes of A type
        /// </summary>
        /// <param name="typeName">The FullName of the type whose Attributes you want</param>
        /// <returns>An object Array of all the attributes</returns>
        public object[] GetAttributes(string typeName)
        {
            return localLoader.GetAttributes(typeName);
        }
        /// <summary>
        /// A string array of all of the Assemblies handled by this PluginManager
        /// </summary>
        public string[] Assemblies
        {
            get { return localLoader.Assemblies; }
        }
        /// <summary>
        /// A string array of all of the <see cref="CoreEventListener"/>s 
        /// handled by this PluginManager
        /// </summary>
        public string[] Listeners
        {
            get { return localLoader.Listeners; }
        }
        /// <summary>
        /// Checks if <paramref name="typeName"/>is part of this assembly
        /// </summary>
        /// <param name="typeName">The TypeName to look for</param>
        /// <returns>Returns True if <paramref name="typeName"/> belongs to any of the assemblies loaded</returns>
        public bool ManagesType(string typeName)
        {
            return localLoader.ManagesType(typeName);
        }
        /// <summary>
        /// Creates an Instance of the <paramref name="typeName"/>
        /// </summary>
        /// <param name="typeName">The FullName of the type to be created</param>
        /// <param name="bindingFlags">Any BindingFlags to create this object</param>
        /// <param name="constructorParams">Any Paramaters that the constructor requires</param>
        /// <returns>A new instance of the class</returns>
        public object CreateInstance(string typeName)
        {
            return localLoader.CreateInstance(typeName);
        }

        /// <summary>
        /// Closes the <see cref="LocalClassLoader"/>
        /// </summary>
        public void Unload()
        {
            localLoader.Unload();
        }
    }
}
